
# Python imports
import logging

# Package imports
import path
import variant
import target

__all__ = [
    'Properties',
    ]



class Properties(dict):


    def add_property(self, var_name, variant_list, value):
        binding_list = self.get(var_name)
        if not binding_list:
            binding_list = [ ]
            self[var_name] = binding_list
        variant_values = variant.VariantValues()
        for v in variant_list:
            variant_values.reconcile(variant.VariantValue(variant.Variant(v[0]), v[1]))
        binding_list.append( (variant_values, value) )

    def remove_property(self, var_name, variant_values, value):
        binding_list = self.get(var_name)
        if binding_list:
            for i in range(len(binding_list)):
                variant = binding_list[i][0]
                # do strong match on variant value
                match = True
                for v in variant_values:
                    if not variant.contains(v[0]) or variant.lookup(v[0]) != v[1]:
                        match = False
                        break
                if match:
                    del binding_list[i]
                    return

    def set_property(self, var_name, variant_list, value):
        """This will replace previous values of the property if found"""
        initial = [ (v[0], None, v[1]) for v in variant_list ]
        variant_values = variant.VariantValues(initial)
        # remove *all* previous definitions of the property
        if self.has_key(var_name):
            binding_list = self[var_name]
            i = 0
            while i < len(binding_list):
                variant = binding_list[i][0]
                # match strongly on variant value
                match = True
                try:
                    for v in variant_values:
                        if not variant.contains(v[0]) or variant.lookup(v[0]) != v[1]:
                            match = False
                            break
                except:
                    pass
                if match:
                    del binding_list[i]
                else:
                    i += 1
        add_property(var_name, variant_list, value)

    def lookup_property(self, variable, variants, scope):
        binding_list = self.get(variable, [])
        result = [ binding[1] for binding in binding_list if self.variant_matches(binding[0], variants, scope) ]
        return result

    def weak_lookup_property(self, variable, variants, scope):
        binding_list = self.get(variable, [])
        return [ binding[1] for binding in binding_list if self.weak_variant_matches(binding[0], variants, scope) ]


    def close_property_match(self, variable, variants, scope):
        """Get the closest match for the value of a variable
        and ignore more generic ones"""
        out = [ ]
        if not self.has_key(variable):
                return out
        binding_list = self[variable]
        # create a list of tuples where first element is the number of matches for those bindings
        # and the second element is the binding value
        match_list = [ ]
        best_match = 0
        for binding in binding_list:
                count = close_variant_matches(binding[0], variants, scope)
                if count > best_match:
                        best_match = count
                match_list.append((count, binding[1]))
        # get the best match(es)
        for match in match_list:
                if match[0] == best_match:
                        out.append(match[1])
        if is_verbose():
                print "Matches found", match_list, "best match:", out
        return out

    def property_value_exists(self, key, value):
        out = self.weak_lookup_property(key, variant.VariantValues(), static_vars.scope)
        for v in out:
            if type(v) == types.ListType:
                for s in v:
                    if s == value:
                        return True
        else:
            if v == value:
                return True
        return False


    def variant_matches(self, prop_variant_values, targ_variant_values, scope):
        for var_name in prop_variant_values.names():
            if var_name == "scope":
                if scope != prop_variant_values.lookup(var_name):
                    return False
            else:
                if not targ_variant_values.contains(var_name):
                    return False
                v1 = targ_variant_values.lookup(var_name)
                v2 = prop_variant_values.lookup(var_name)
                if v1 != v2:
                    return False
        return True

    def weak_variant_matches(prop_variant_values, targ_variant_values, scope):
        for var_name in prop_variant_values.names():
            # match scope strongly
            if var_name == "scope":
                if scope != prop_variant_values.lookup(var_name):
                    return False
            elif targ_variant_values.contains(var_name):
                v1 = targ_variant_values.lookup(var_name)
                v2 = prop_variant_values.lookup(var_name)
                if not v1 == v2:
                    return False
        return True

    def dump_property_table_key(self, key):
        logging.debug(_("Entries for %s:"), key)
        binding_list = self[key]
        for binding in binding_list:
            logging.debug(_("  If %s\n    then %s"), str(binding[0]), binding[1])

    def dump_property_table(self):
        for key in self.keys():
            self.dump_property_table_key(key)
